package main

import (
	"debug/pe"
	"strconv"
)

const fileFilter = "All File(*);;Relocatable File(*.o *.obj)"

type Root = CoffFile

type CoffFile struct {
	FileHeader     ImageFileHeader
	OptionalHeader []byte
	Section        []ImageSectionHeader
	SectionData    []ImageSectionData
	Relocation     []ImageRelocation
	LineNum        []ImageLineNumber
	Symbol         []ImageSymbol
	StringTable    ImageStringTable
}

func (v *CoffFile) Is64BitFile() bool {
	return v.FileHeader.Characteristics&pe.IMAGE_FILE_32BIT_MACHINE == 0
}

func (v *CoffFile) Unmarshal(dec *Decoder, info *ValueInfo) {
	dec.SetArg(v)
	dec.Decode(&v.FileHeader, &ValueInfo{name: "FileHeader"})
	if v.FileHeader.SizeOfOptionalHeader > 0 {
		newDec := dec.SubDecoder(int(v.FileHeader.SizeOfOptionalHeader))
		newDec.Decode(&v.OptionalHeader, &ValueInfo{name: "OptionalHeader"})
		dec.Seek(newDec.Pos())
	}
	sectNum := v.FileHeader.NumberOfSections
	v.Section = make([]ImageSectionHeader, sectNum)
	dec.Decode(&v.Section, &ValueInfo{name: "Section"})
	v.SectionData = make([]ImageSectionData, sectNum)
	dec.Decode(&v.SectionData, &ValueInfo{name: "SectionData"})
	off := dec.Pos() / 8
	if off&7 != 0 {
		var space byte
		dec.Decode(&space, &ValueInfo{name: "Space"})
	}
	v.Relocation = make([]ImageRelocation, sectNum)
	dec.Decode(&v.Relocation, &ValueInfo{name: "Relocation"})
	v.LineNum = make([]ImageLineNumber, sectNum)
	dec.Decode(&v.LineNum, &ValueInfo{name: "LineNum"})
	v.Symbol = make([]ImageSymbol, v.FileHeader.NumberOfSymbols)
	dec.Decode(&v.Symbol, &ValueInfo{name: "Symbol"})
	dec.Decode(&v.StringTable, &ValueInfo{name: "StringTable"})
}

/****************** 文件头 **********************/

type ImageFileHeader struct {
	Machine              ImageFileMachine   `info:"运行平台"`
	NumberOfSections     uint16             `info:"区块数目"`
	TimeDateStamp        TimeStamp          `info:"文件创建日期和时间"`
	PointerToSymbolTable uint32             `info:"符号表地址"`
	NumberOfSymbols      uint32             `info:"符号表中符号个数"`
	SizeOfOptionalHeader uint16             `info:"可选头大小"`
	Characteristics      ImageFileCharacter `info:"文件属性"`
}

type ImageFileMachine uint16

func (v ImageFileMachine) String() string {
	textMap := map[uint16]string{
		0x0000: "unknown",                       // IMAGE_FILE_MACHINE_UNKNOWN
		0x01d3: "Matsushita_AM33",               // IMAGE_FILE_MACHINE_AM33
		0x8664: "x64",                           // IMAGE_FILE_MACHINE_AMD64
		0x01c0: "ARM",                           // IMAGE_FILE_MACHINE_ARM
		0x01c4: "ARM_NT",                        // IMAGE_FILE_MACHINE_ARMNT
		0xaa64: "ARM64",                         // IMAGE_FILE_MACHINE_ARM64
		0x0ebc: "EFI_byte_code",                 // IMAGE_FILE_MACHINE_EBC
		0x014c: "Intel_386_or_later",            // IMAGE_FILE_MACHINE_I386
		0x0200: "Intel_Itanium",                 // IMAGE_FILE_MACHINE_IA64
		0x6232: "Loong_Arch32",                  // IMAGE_FILE_MACHINE_LOONGARCH32
		0x6264: "Loong_Arch64",                  // IMAGE_FILE_MACHINE_LOONGARCH64
		0x9041: "Mitsubishi_M32R_little_endian", // IMAGE_FILE_MACHINE_M32R
		0x0266: "MIPS16",                        // IMAGE_FILE_MACHINE_MIPS16
		0x0366: "MIPS_with_FPU",                 // IMAGE_FILE_MACHINE_MIPSFPU
		0x0466: "MIPS16_with_FPU",               // IMAGE_FILE_MACHINE_MIPSFPU16
		0x01f0: "Power_PC_little_endian",        // IMAGE_FILE_MACHINE_POWERPC
		0x01f1: "Power_PC_with_floating_point",  // IMAGE_FILE_MACHINE_POWERPCFP
		0x0166: "MIPS_little_endian",            // IMAGE_FILE_MACHINE_R4000
		0x01a2: "Hitachi_SH3",                   // IMAGE_FILE_MACHINE_SH3
		0x01a3: "Hitachi_SH3_DSP",               // IMAGE_FILE_MACHINE_SH3DSP
		0x01a6: "Hitachi_SH4",                   // IMAGE_FILE_MACHINE_SH4
		0x01a8: "Hitachi_SH5",                   // IMAGE_FILE_MACHINE_SH5
		0x01c2: "Thumb",                         // IMAGE_FILE_MACHINE_THUMB
		0x0169: "MIPS_little_endian_WCE_v2",     // IMAGE_FILE_MACHINE_WCEMIPSV2
	}
	if text, ok := textMap[uint16(v)]; ok {
		return text
	}
	return strconv.Itoa(int(v))
}

type ImageFileCharacter uint16

func (v ImageFileCharacter) String() string {
	// "IMAGE_FILE_" + textMap
	textMap := []string{
		0:  "RELOCS_STRIPPED",
		1:  "EXECUTABLE_IMAGE",
		2:  "LINE_NUMS_STRIPPED",
		3:  "LOCAL_SYMS_STRIPPED",
		4:  "AGGRESIVE_WS_TRIM",
		5:  "LARGE_ADDRESS_AWARE",
		7:  "BYTES_REVERSED_LO",
		8:  "32BIT_MACHINE",
		9:  "DEBUG_STRIPPED",
		10: "REMOVABLE_RUN_FROM_SWAP",
		11: "NET_RUN_FROM_SWAP",
		12: "SYSTEM",
		13: "DLL",
		14: "UP_SYSTEM_ONLY",
		15: "BYTES_REVERSED_HI",
	}
	return fieldString(textMap, uint64(v), 16)
}

/****************** 节区表 **********************/

type ImageSectionHeader struct {
	Name                 SectionName      `info:"区块名"`
	VirtualSize          uint32           `info:"区块大小"`
	VirtualAddress       uint32           `info:"内存装载地址"`
	SizeOfRawData        uint32           `info:"文件占用大小"`
	PointerToRawData     uint32           `info:"文件偏移地址"`
	PointerToRelocations uint32           `info:"重定位偏移"`
	PointerToLinenumbers uint32           `info:"行号表偏移"`
	NumberOfRelocations  uint16           `info:"重定位项数"`
	NumberOfLinenumbers  uint16           `info:"行号表数目"`
	Characteristics      SectionCharacter `info:"区块属性"`
}

func (v *ImageSectionHeader) String() string {
	return v.Name.String()
}

type SectionName []byte

func (v *SectionName) Unmarshal(dec *Decoder, info *ValueInfo) {
	*v = dec.ReadBytes(8)
}

func (v SectionName) String() string {
	n := 0
	for n < len(v) && v[n] != 0 {
		n++
	}
	return string(v[:n])
}

type SectionCharacter uint32

func (v SectionCharacter) String() string {
	// "IMAGE_SCN_" + textMap
	textMap := []string{
		0:  "TYPE_DSECT",
		1:  "TYPE_NOLOAD",
		2:  "TYPE_GROUP",
		3:  "TYPE_NO_PAD",
		4:  "TYPE_COPY",
		5:  "CNT_CODE",
		6:  "CNT_INITIALIZED_DATA",
		7:  "CNT_UNINITIALIZED_DATA",
		8:  "LNK_OTHER",
		9:  "LNK_INFO",
		10: "TYPE_OVER",
		11: "LNK_REMOVE",
		12: "LNK_COMDAT",
		15: "MEM_FARDATA",
		17: "MEM_16BIT",
		18: "MEM_LOCKED",
		19: "MEM_PRELOAD",

		20: "ALIGN_1BYTES",
		21: "ALIGN_2BYTES",
		22: "ALIGN_8BYTES",
		23: "ALIGN_128BYTES",

		24: "LNK_NRELOC_OVFL",
		25: "MEM_DISCARDABLE",
		26: "MEM_NOT_CACHED",
		27: "MEM_NOT_PAGED",
		28: "MEM_SHARED",
		29: "MEM_EXECUTE",
		30: "MEM_READ",
		31: "MEM_WRITE",
	}
	return fieldString(textMap, uint64(v), 32)
}

/****************** 节区 **********************/

type ImageSectionData struct {
	Data  []byte
	index int
	name  string
}

func (v *ImageSectionData) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*CoffFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	var err error
	v.index, err = strconv.Atoi(info.name)
	if err != nil {
		panic(err)
	}

	off := (dec.Pos() + 7) / 8
	if f.Section[v.index].PointerToRawData < uint32(off) {
		return
	} else if f.Section[v.index].PointerToRawData > uint32(off) {
		panic("unknow section at: " + strconv.Itoa(off))
	}

	length := f.Section[v.index].SizeOfRawData
	size := f.Section[v.index].VirtualSize
	if size > 0 && size <= length {
		v.Data = dec.ReadBytes(int(size))
	} else {
		v.Data = dec.ReadBytes(int(length))
	}
	v.name = f.Section[v.index].Name.String()
}

func (v ImageSectionData) String() string {
	return v.name
}

/****************** 重定位表 **********************/

type ImageRelocation struct {
	Relocation []BaseRelocation
	index      int
	name       string
}

func (v *ImageRelocation) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*CoffFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	var err error
	v.index, err = strconv.Atoi(info.name)
	if err != nil {
		panic(err)
	}

	v.name = f.Section[v.index].Name.String()
	if f.Section[v.index].NumberOfRelocations == 0 {
		return
	}
	if f.Section[v.index].PointerToRelocations != uint32(dec.Pos()/8) {
		panic("unknow relocation at: " + strconv.Itoa(dec.Pos()/8))
	}
	v.Relocation = make([]BaseRelocation, f.Section[v.index].NumberOfRelocations)
	dec.Decode(&v.Relocation, info)
}

func (v *ImageRelocation) String() string {
	return v.name
}

type BaseRelocation struct {
	Address uint32    `info:"定位偏移"`
	Symbol  uint32    `info:"符号"`
	Type    RelocType `info:"类型"`
}

type RelocType uint16

func (v RelocType) String() string {
	switch v {
	case 6:
		return "RELOC_ADDR32"
	case 20:
		return "RELOC_REL32"
	default:
		return strconv.Itoa(int(v))
	}
}

/****************** 行号表 **********************/

type ImageLineNumber struct {
	LineNum []BaseLineNumber
	index   int
	name    string
}

func (v *ImageLineNumber) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*CoffFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	var err error
	v.index, err = strconv.Atoi(info.name)
	if err != nil {
		panic(err)
	}

	v.name = f.Section[v.index].Name.String()
	if f.Section[v.index].NumberOfLinenumbers == 0 {
		return
	}
	if f.Section[v.index].PointerToLinenumbers != uint32(dec.Pos()/8) {
		panic("unknow line number at: " + strconv.Itoa(dec.Pos()/8))
	}
	v.LineNum = make([]BaseLineNumber, f.Section[v.index].NumberOfLinenumbers)
	dec.Decode(&v.LineNum, info)
}

func (v *ImageLineNumber) String() string {
	return v.name
}

type BaseLineNumber struct {
	AddrOrSymbol uint32
	LineNo       uint16
}

/****************** 符号表 **********************/

type ImageSymbol struct {
	*BaseSymbol
	AuxData []byte
}

func (v *ImageSymbol) Unmarshal(dec *Decoder, info *ValueInfo) {
	f, ok := dec.Arg().(*CoffFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	index, err := strconv.Atoi(info.name)
	if err != nil {
		panic(err)
	}

	for i, sym := range f.Symbol {
		if sym.BaseSymbol == nil || sym.NumberOfAuxSymbols == 0 {
			continue
		}
		if i >= index {
			break
		}
		if i+int(sym.NumberOfAuxSymbols) == index {
			v.AuxData = dec.ReadBytes(18)
			info.info = "附加信息"
			return
		}
	}

	v.BaseSymbol = &BaseSymbol{}
	dec.Decode(&v.BaseSymbol, info)
}

func (v *ImageSymbol) String() string {
	if v.BaseSymbol == nil {
		return ""
	}
	return string(v.Name)
}

type BaseSymbol struct {
	Name               SymbolName  `info:"名称"`
	Value              uint32      `info:"取值"`
	SectionNumber      int16       `info:"所在节区"`
	Type               uint16      `info:"类型"`
	StorageClass       SymbolClass `info:"存储类型"`
	NumberOfAuxSymbols uint8       `info:"附加记录数"`
}

type SymbolName string

func (v *SymbolName) Unmarshal(dec *Decoder, info *ValueInfo) {
	pos := dec.Pos()
	bs := dec.ReadBytes(8)
	if bs[0] == 0 && bs[1] == 0 && bs[2] == 0 && bs[3] == 0 {
		var tmp uint32
		dec.Seek(pos)
		dec.Decode(&tmp, &ValueInfo{name: "Zero"})
		dec.Decode(&tmp, &ValueInfo{name: "Offset"})
		*v = SymbolName(StringFromTable(dec, tmp))
		return
	}

	n := 0
	for n < len(bs) && bs[n] != 0 {
		n++
	}
	*v = SymbolName(bs[:n])
}

type SymbolClass uint8

func (v SymbolClass) String() string {
	textMap := map[uint8]string{
		0:   "NULL",             // 无存储类型
		1:   "AUTOMATIC",        // 自动类型,通常是在栈中分配的变量
		2:   "EXTERNAL",         // 外部符号。当为外部符号时，iSection的值应该为0，如果不为0，则ulValue为符号在段中的偏移
		3:   "STATIC",           // 静态类型。ulValue为符号在段中的偏移。如果偏移为0，那么这个符号代表段名。
		4:   "REGISTER",         // 寄存器变量
		8:   "MEMBER_OF_STRUCT", // 结构成员。ulValue值为该符号在结构中的顺序
		10:  "STRUCT_TAG",       // 结构标识符
		11:  "MEMBER_OF_UNION",  // 联合成员。ulValue值为该符号在联合中的顺序
		12:  "UNION_TAG",        // 联合标识符
		13:  "TYPE_DEFINITION",  // 类型定义
		101: "FUNCTION",         // 函数名
		102: "FILE",             // 文件名
	}
	if text, ok := textMap[uint8(v)]; ok {
		return text
	}
	return strconv.Itoa(int(v))
}

/****************** 字符串表 **********************/

func StringFromTable(dec *Decoder, off uint32) string {
	if off == 0 {
		return ""
	}
	f, ok := dec.Arg().(*CoffFile)
	if !ok || f == nil {
		panic("decode arg not set")
	}

	pos := dec.Pos()
	defer dec.Seek(pos)

	str := ""
	strOff := f.FileHeader.PointerToSymbolTable + f.FileHeader.NumberOfSymbols*18
	dec.Seek(int(strOff+off) * 8)
	dec.Decode(&str, nil)
	return str
}

type ImageStringTable struct {
	Size    uint32
	Strings []string
}
